﻿using Prism.Commands;
using Prism.Mvvm;
using Simple_search.Data;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Windows.Media;
using Simple_search.Service;
using ClosedXML;
using ClosedXML.Excel;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO;
using Prism.Services.Dialogs;
using Prism.Events;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using System.Threading.Tasks;

namespace Simple_search.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public MainWindowViewModel(IDialogService dialogService, SearchManager searchManager, EventAggregator eventAggregator)
        {
            _dialogService = dialogService;
            _searchManager = searchManager;
            _eventAggregator = eventAggregator;
            SearchUnits.CollectionChanged += SearchUnits_CollectionChanged;
            _eventAggregator.GetEvent<EventStatusChange>().Subscribe(GetBackgroundStatus);
            Initialize();
            _eventAggregator.GetEvent<EventStatusChange>().Publish(System.Tuple.Create(BackgroundStatus.Idle, ""));
        }

        private void Initialize()
        {
            _eventAggregator.GetEvent<EventStatusChange>().Publish(System.Tuple.Create(BackgroundStatus.Initialize, ""));
        }

        private IDialogService _dialogService;
        private SearchManager _searchManager;
        private EventAggregator _eventAggregator;

        private void GetBackgroundStatus(Tuple<BackgroundStatus, string> value)
        {
            this.backgroundStatus = value.Item1;
            CurrentText = value.Item2;
            switch (value.Item1)
            {
                case BackgroundStatus.Initialize:
                    CurrentStatus = "初始化";
                    break;
                case BackgroundStatus.Idle:
                    CurrentStatus = "空闲";
                    break;
                case BackgroundStatus.Searching:
                    CurrentStatus = "检索中";
                    break;
                case BackgroundStatus.Merge:
                    CurrentStatus = "运算中";
                    break;
            }
            CommandAddSearchUnit.RaiseCanExecuteChanged();
            CommandSearch.RaiseCanExecuteChanged();
        }

        private BackgroundStatus backgroundStatus;

        private void SearchUnits_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            CommandSearch.RaiseCanExecuteChanged();
        }

        public List<ColorWithBrush> ColorList { get; set; } = ColorPicker.FormColorList();

        private string currentText;
        public string CurrentText
        {
            get { return currentText; }
            set { SetProperty(ref currentText, value); }
        }

        private string currentStatus;
        public string CurrentStatus
        {
            get { return currentStatus; }
            set { SetProperty(ref currentStatus, value); }
        }

        private string searchUnitName = "";
        public string SearchUnitName
        {
            get { return searchUnitName; }
            set { 
                SetProperty(ref searchUnitName, value); 
                this.CommandAddSearchUnit.RaiseCanExecuteChanged();
            }
        }

        private string searchTermString = "";
        public string SearchTermString
        {
            get { return searchTermString; }
            set {
                SetProperty(ref searchTermString, value);
                this.CommandAddSearchUnit.RaiseCanExecuteChanged();
            }
        }

        private ColorWithBrush? markColor;
        public ColorWithBrush? MarkColor
        {
            get { return markColor; }
            set {
                SetProperty(ref markColor, value);
                CommandAddSearchUnit.RaiseCanExecuteChanged();
            }
        }

        public ObservableCollection<SearchUnit> SearchUnits { get; set; } = new();

        private DelegateCommand commandAddSearchUnit;
        public DelegateCommand CommandAddSearchUnit =>
            commandAddSearchUnit ?? (commandAddSearchUnit = new DelegateCommand(ExecuteCommandAddSearchUnit, CanExecuteCommandAddSearchUnit));

        bool CanExecuteCommandAddSearchUnit()
        {
            if (this.backgroundStatus != BackgroundStatus.Idle)
            {
                return false;
            }
            if ((this.SearchUnitName != "") && (this.SearchTermString != "") && (this.MarkColor is not null))
            {
                return true;
            } else
            {
                return false; 
            }
        }

        void ExecuteCommandAddSearchUnit()
        {
            this.SearchUnits.Add(new SearchUnit(this.SearchUnitName, this.SearchTermString, this.MarkColor));
            this.SearchUnitName = "";
            this.SearchTermString = "";
            this.MarkColor = null;
        }

        private DelegateCommand commandSearch;
        public DelegateCommand CommandSearch =>
            commandSearch ?? (commandSearch = new DelegateCommand(ExecuteCommandSearch, CanExecuteCommandSearch));

        async void ExecuteCommandSearch()
        {
            DialogParameters parameters = new();
            parameters.Add("SearchUnitCount", SearchUnits.Count);
            _dialogService.ShowDialog("option", parameters, async r =>
            {
                if (r.Result == ButtonResult.OK)
                {
                    string bookDirectory = r.Parameters.GetValue<string>("BookDirectory");
                    string fileName = r.Parameters.GetValue<string>("FileName");
                    bool isFuzzy = r.Parameters.GetValue<bool>("IsFuzzy");
                    int threshold = r.Parameters.GetValue<int>("Threshold");
                    int blockSize = r.Parameters.GetValue<int>("BlockSize");
                    int resultCount;

                    await new TaskFactory().StartNew(() => _searchManager.SetProfile(bookDirectory, blockSize, threshold, isFuzzy));

                    await new TaskFactory().StartNew(() => _searchManager.Search(SearchUnits.ToList<SearchUnit>()));

                    Dictionary<IBook, List<BlockResult>> result = _searchManager.GetSearchResult();
                    _searchManager.Clear();


                    Parameters tempParameters = new Parameters() { FileName = fileName, Result = result };
                    await new TaskFactory().StartNew(() => PrintToExcel(tempParameters));
                   
                    parameters.Add("OutputFilePath", fileName);
                    parameters.Add("ResultCount", tempParameters.RowIndex - 3);
                    _dialogService.ShowDialog("finish", parameters, r => { });

                    result = null;
                    foreach (SearchUnit searchUnit in SearchUnits)
                    {
                        searchUnit.Result.Clear();
                    }
                    _eventAggregator.GetEvent<EventStatusChange>().Publish(System.Tuple.Create(BackgroundStatus.Idle, ""));
                }
            });
        }

        bool CanExecuteCommandSearch()
        {
            if (this.backgroundStatus != BackgroundStatus.Idle)
            {
                return false;
            }
            if (this.SearchUnits.Count > 0)
            {
                return true;
            }
            else
            { 
                return false; 
            }
        }

        private void PrintToExcel(Parameters parameters)
        {
            using (FileStream stream = File.Create(parameters.FileName))
            {
                using (XLWorkbook workbook = new XLWorkbook())
                {
                    IXLWorksheet worksheet = workbook.Worksheets.Add("检索结果");
                    worksheet.Cell(1, 1).Value = "书名";
                    worksheet.Cell(1, 2).Value = "检索项";
                    worksheet.Cell(1, 3).Value = "检索项数";
                    worksheet.Cell(1, 4).Value = "内容";

                    foreach (IBook book in parameters.Result.Keys)
                    {
                        using (book.Open())
                        {
                            foreach (BlockResult block in parameters.Result[book])
                            {
                                block.PrintToExcel(worksheet, parameters.RowIndex);
                                block.Dispose();
                                parameters.RowIndex++;
                            }
                        }
                    }
                    workbook.SaveAs(stream);
                }
            }
        }

        private class Parameters
        {
            public int RowIndex { get; set; } = 2;
            public Dictionary<IBook, List<BlockResult>> Result { get; set; }
            public string FileName { get; set; }
        }
    }
}
